#!/usr/bin/env python
""" AnyBackup is a windows specific backup program to keep multiple volumes in sync with multiple volumes
    Copyright (C) 2011  Andrew Hopkinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Exported Classes:

    easyListCtrl -- a wrapper for ListCtrl which transparently gives the easy of use found in ListBox but with all the
                    bells and whistles of ListCtrl
    """

import wx
import os
import DriveUtil
from enum import AEnums
from sql import SQLLite
import datetime


class DictView(wx.ListCtrl):
    def __init__(self, parent, id=-1, position=wx.DefaultPosition, size=wx.DefaultSize,
                 style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES | wx.LC_VIRTUAL, hideHeaders=True):
        super(DictView, self).__init__(parent, id, position, size, style)
        self.items = {}
        self.itemList = []
        self.InsertColumn(0, 'Key', wx.LIST_FORMAT_LEFT)
        self.InsertColumn(1, 'Value', wx.LIST_FORMAT_LEFT, width=size.width-90)
        self.SetSingleStyle(wx.LC_NO_HEADER, hideHeaders)
        self.SetItemCount(0)

    def updateDict(self, pairs):
        for k, v in pairs:
            new = k not in self.items
            if new:
                self.itemList.append((k, v))
                self.items[k] = self.itemList.index((k, v))
            else:
                self.itemList[self.items[k]] = (k, v)
            self.SetItemCount(len(self.itemList))
            self.RefreshItem(self.items[k])

    def OnGetItemText(self, item, col):
        return self.itemList[item][col]

    def OnGetItemAttr(self, item):
        return None

    def OnGetItemImage(self, item):
        return -1

    def adjust(self):
        for i in xrange(2):
            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)


class easyListCtrl(object):
    """ a wrapper for ListCtrl which transparently gives the ease of use found in ListBox
        but with all the bells and whistles of ListCtrl """
    DRIVE = 'drive'
    FILE = 'file'
    STRING = 'string'

    def __init__(self, parent, wxId, fileUtil, mode=None, single=True):
        self.parent = parent
        self.mapping = {}
        self.fileUtil = fileUtil
        self.count = 0
        self.mode = mode
        self.itemDataMap = {}
        self.auto = False
        self.single = single
        self.r = SQLLite()
        if not mode:
            self.mode = self.STRING
        style = wx.LC_REPORT
        if self.single:
            style |= wx.LC_SINGLE_SEL
        self.listCtrl = wx.ListCtrl(parent, wxId, wx.DefaultPosition, wx.DefaultSize, style)
        self.setMode(self.mode)
        self.listCtrl.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)

    # noinspection PyUnusedLocal
    def startDrag(self, evt):
        if self.mode != self.FILE:
            return
        data = wx.FileDataObject()
        files = self.GetSelectedData()
        if type(files) != list:
            files = [files]
        if not all([type(x) == dict for x in files]):
            return
        paths = [x for x in self.r.getFullPaths(files) if x]
        if not paths:
            return
        [data.AddFile(x) for x in paths]
        if not data.GetFilenames():
            return
        dropSource = wx.DropSource(self.listCtrl)
        dropSource.SetData(data)
        dropSource.DoDragDrop(flags=wx.Drag_CopyOnly)

    def GetSelectedData(self):
        data = []
        selected = -1
        while True:
            selected = self.listCtrl.GetNextItem(selected, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
            if selected >= 0:
                selectedItem = self.listCtrl.GetItem(selected)
                if selectedItem and self.single:
                    return self.mapping[selectedItem.GetData()]
                else:
                    data.append(self.mapping[selectedItem.GetData()])
            else:
                break
        if self.single:
            return None
        else:
            return data

    def SetImageList(self, imageList, fileType):
        return self.listCtrl.SetImageList(imageList, fileType)

    def getWidth(self):
        width = 0
        for column in xrange(self.listCtrl.GetColumnCount()):
            width += self.listCtrl.GetColumnWidth(column)
        return width

    def getListCtrl(self):
        return self.listCtrl

    def getMode(self):
        return self.mode

    # noinspection PyBroadException
    def setMode(self, mode):
        self.count = 0
        self.mapping = {}
        self.columns = 1
        columnNames = None
        hideColumns = None
        columnFormats = None
        self.mode = mode
        self.listCtrl.ClearAll()
        if mode == self.DRIVE:
            self.columns = 4
            columnNames = ['Name', 'Free', 'Total', 'Chkdsk']
            hideColumns = False
            columnFormats = [wx.LIST_FORMAT_LEFT, wx.LIST_FORMAT_RIGHT, wx.LIST_FORMAT_RIGHT, wx.LIST_FORMAT_RIGHT]
        elif mode == self.FILE:
            self.columns = 3
            columnNames = ['Name', 'Size', 'Modified Time']
            hideColumns = False
            columnFormats = [wx.LIST_FORMAT_LEFT, wx.LIST_FORMAT_RIGHT, wx.LIST_FORMAT_RIGHT]
        elif mode == self.STRING:
            hideColumns = True
        if hideColumns:
            self.listCtrl.SetSingleStyle(wx.LC_NO_HEADER, True)
        else:
            self.listCtrl.SetSingleStyle(wx.LC_NO_HEADER, False)
        for i in xrange(self.columns):
            try:
                name = columnNames[i]
            except:
                name = ''
            try:
                colFormat = columnFormats[i]
            except:
                colFormat = wx.LIST_FORMAT_LEFT
            self.listCtrl.InsertColumn(i, name, colFormat)

    def populateDrives(self, drives):
        driveObjs = DriveUtil.getDriveDictionary()
        drives = sorted(drives, key=lambda x: x[AEnums.NAME], reverse=True)
        content = [x for x in drives if x[AEnums.TYPE] == AEnums.CONTENT]
        backup = [x for x in drives if x[AEnums.TYPE] == AEnums.BACKUP]
        for drive in backup + content:
            d = drive
            newItem = wx.ListItem()
            free = "%.2f" % (float(d[AEnums.FREESPACE]) / float(1048576))
            total = "%.2f" % (float(d[AEnums.TOTALSPACE]) / float(1048576))
            freeStr = str(free) + 'GB'
            totalStr = str(total) + 'GB'
            extra = {
                1: freeStr,
                2: totalStr,
                3: d[AEnums.CHKDSK],
            }
            newItem.SetText(d[AEnums.NAME])
            fullPerc = float(d[AEnums.FREESPACE]) * 100 / float(d[AEnums.TOTALSPACE])
            color = wx.GREEN
            if fullPerc <= 10:
                color = '#B40404'
            elif fullPerc <= 15:
                color = 'YELLOW'
            newItem.SetBackgroundColour(color)
            if self.r.isLocked(d) and d[AEnums.TYPE] == AEnums.BACKUP:
                newItem.SetImage(self.fileUtil.getLocked())
            elif (d[AEnums.NAME] + d[AEnums.SERIAL]) in driveObjs:
                newItem.SetImage(self.fileUtil.getConnected())
            else:
                newItem.SetImage(self.fileUtil.getDisconnected())
            if not d[AEnums.TYPE] == AEnums.BACKUP:
                newItem.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD))
            self.InsertItem(newItem, drive, extra=extra)
            self.itemDataMap[newItem.GetData()] = (d[AEnums.NAME], float(free), float(total),
                datetime.datetime.strptime(d[AEnums.CHKDSK], '%Y-%m-%d') if d[AEnums.CHKDSK] else None)
        self.adjust()

    def populateStrings(self, strings):
        for string in strings:
            newItem = wx.ListItem()
            #newItem.SetText(unicode(string).encode('UTF-16'))
            newItem.SetText(string)
            newItem.SetTextColour(wx.RED)
            newItem.SetImage(self.fileUtil.getError())
            self.InsertItem(newItem, string)
            self.itemDataMap[newItem.GetData()] = [string]
        self.adjust()

    def getItemDataMap(self):
        return self.itemDataMap

    def populateFiles(self, files, fileRefIn=None, showDirs=False):
        for fileRef in files:
            newItem = wx.ListItem()
            if showDirs:
                try:
                    newItem.SetText(
                        unicode(fileRef[AEnums.PREFIX] + os.sep + fileRef[AEnums.PATH] + os.sep +
                                fileRef[AEnums.NAME]).encode(
                                    'utf-8'))
                except UnicodeEncodeError:
                    continue
            else:
                newItem.SetText(unicode(fileRef[AEnums.NAME]).encode('utf-8'))
            newItem.SetImage(self.fileUtil.getIcon(fileRef))
            if fileRef[AEnums.DIR] == str(True):
                newItem.SetTextColour(wx.BLUE)
                self.InsertItem(newItem, fileRef)
                self.itemDataMap[newItem.GetData()] = [newItem.GetText(), 0, datetime.datetime.min()]
            else:
                size = "%.2f" % (float(fileRef[AEnums.SIZE]) / float(1024))
                sizeStr = str(size)
                sizeStr += 'MB'
                dateStr = datetime.datetime.fromtimestamp(
                    fileRef[AEnums.MTIME]).strftime('%m-%d-%Y %H:%M:%S')
                extra = {1: sizeStr, 2: dateStr}
                self.InsertItem(newItem, data=fileRef, extra=extra)
                self.itemDataMap[newItem.GetData()] = [newItem.GetText(), float(size), fileRef[AEnums.MTIME]]


        if fileRefIn is not None:
            if AEnums.PARENT in fileRefIn:
                p = self.r.getFile(fileRefIn[AEnums.PARENT])
                if not p:
                    p = self.parent.buManager.getDrive(fileRefIn)
                newItem = wx.ListItem()
                newItem.SetText("..")
                newItem.SetImage(self.fileUtil.typeIndex['folder'])
                newItem.SetTextColour(wx.BLUE)
                self.InsertItem(newItem, p)

        self.adjust()

    def setSelectedDrive(self, fileRef):
        for i in range(0, self.listCtrl.GetItemCount()):
            ref = self.mapping[self.listCtrl.GetItem(i).GetData()]
            if ref.getName() == fileRef.getVolName() and ref.GetSerial() == fileRef.getSerial():
                self.listCtrl.SetItemState(i, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
                self.auto = True
                return

    def setSelectedFiles(self, fileRef):
        for i in range(0, self.listCtrl.GetItemCount()):
            ref = self.mapping[self.listCtrl.GetItem(i).GetData()]
            if ref.getName() == fileRef.getName() and ref.getDir() == fileRef.getDir():
                self.auto = True
                self.listCtrl.SetItemState(1, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
                return

    def isAutoSelect(self):
        if self.auto:
            self.auto = False
            return True
        return False

    def DeleteAllItems(self):
        self.count = 0
        self.mapping = {}
        return self.listCtrl.DeleteAllItems()

    def GetNextItem(self, item, options):
        return self.listCtrl.GetNextItem(item, options)

    def InsertItem(self, item, data=None, extra=None):
        self.mapping[self.count] = data
        item.SetData(self.count)
        wxId = item.GetId()
        self.count += 1
        result = self.listCtrl.InsertItem(item)
        if extra:
            for col in extra:
                self.listCtrl.SetStringItem(wxId, col, extra[col])
        return result

    def GetData(self, item):
        data = self.mapping[item.GetData()]
        return data

    def adjust(self):
        for i in xrange(self.columns):
            self.listCtrl.SetColumnWidth(i, wx.LIST_AUTOSIZE)
